Skip to content

[Repo Assist] feat: add LazyList.consLazy for constructing from Lazy(LazyList<'T)>#239

Draft
github-actions[bot] wants to merge 2 commits intomasterfrom
repo-assist/improve-lazylist-conslazy-23c4939b68642ec5
Draft

[Repo Assist] feat: add LazyList.consLazy for constructing from Lazy(LazyList<'T)>#239
github-actions[bot] wants to merge 2 commits intomasterfrom
repo-assist/improve-lazylist-conslazy-23c4939b68642ec5

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 9, 2026

🤖 This PR was created by Repo Assist, an automated AI assistant.

Implements the enhancement requested in #134.

Summary

LazyList.consDelayed accepts unit -> LazyList<'T> (a thunk). When a Lazy(LazyList<'T)> value is already available, wrapping it in fun () -> tail.Value works but is unnecessarily wasteful — it allocates an extra closure and forgoes .NET's built-in Lazy caching.

consLazy provides the direct construction:

val consLazy : 'T -> Lazy(LazyList<'T)> -> LazyList<'T>

Usage example (infinite list of 1s, as shown in #134):

let rec ones: LazyList(int) = LazyList.consLazy 1 (lazy ones)

Implementation

let consLazy x (l: Lazy(LazyList<'T)>) =
    lzy(fun () -> consc x l.Value)

The outer lzy keeps the head cell lazy until the list is consumed. l.Value is the standard .NET Lazy(T) evaluation, which caches the result and is evaluated at most once.

Changes

  • src/FSharpx.Collections/LazyList.fs — add consLazy
  • src/FSharpx.Collections/LazyList.fsi — add signature + doc comment
  • tests/FSharpx.Collections.Tests/LazyListTests.fs — 4 new tests: head access, toList, lazy divergence check (tail not evaluated on construction), and infinite list

Test Status

✅ All 710 tests pass (Passed: 710, Skipped: 6, Failed: 0). The 6 skipped tests are pre-existing skips unrelated to this change.

Related to #134

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f

Implements the enhancement requested in #134. consLazy takes a head
value and a Lazy<LazyList<'T>> tail, providing a more efficient
alternative to consDelayed when a Lazy value is already available.

Example use:
  let rec ones: LazyList<int> = LazyList.consLazy 1 (lazy ones)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gdziadkiewicz
Copy link
Collaborator

/repo-assist resolve conflicts

@gdziadkiewicz gdziadkiewicz requested review from Copilot and gdziadkiewicz and removed request for Copilot March 14, 2026 13:13
@gdziadkiewicz gdziadkiewicz self-assigned this Mar 14, 2026
@gdziadkiewicz gdziadkiewicz requested a review from Copilot March 14, 2026 13:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new LazyList.consLazy constructor to build a LazyList<'T> from a head value and an existing Lazy<LazyList<'T>> tail, aligning the public API with the underlying representation discussed in #134.

Changes:

  • Add LazyList.consLazy implementation in LazyList.fs.
  • Expose consLazy in the public signature file LazyList.fsi with documentation.
  • Add new unit tests covering basic consLazy behavior and an infinite-list scenario.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/FSharpx.Collections/LazyList.fs Introduces the new consLazy constructor implementation.
src/FSharpx.Collections/LazyList.fsi Exposes consLazy publicly and updates signature formatting in this file.
tests/FSharpx.Collections.Tests/LazyListTests.fs Adds tests for consLazy behavior (head, toList, divergence-on-construction, infinite list).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 29 to 39
///O(1). Test if a list is empty. Forces the evaluation of
/// the first element of the stream if it is not already evaluated.
member IsEmpty : bool
member IsEmpty: bool

///O(1). Return the first element of the list. Forces the evaluation of
/// the first cell of the list if it is not already evaluated.
member Head : 'T
member Head: 'T

///O(n). Return the length of the list
member Length : unit -> int
member Length: unit -> int


val (|Cons|Nil|) : LazyList<'T> -> Choice<('T * LazyList<'T>),unit>

val (|Cons|Nil|): LazyList<'T> -> Choice<('T * LazyList<'T>), unit>
lzy(fun () -> (consc x (lzy(fun () -> (force(l()))))))

let consLazy x (l: Lazy<LazyList<'T>>) =
lzy(fun () -> consc x l.Value)
Comment on lines +331 to +332
// tail is not evaluated unless the tail is consumed
Expect.isTrue "consLazy divergence" (let _ = LazyList.consLazy 1 (lazy (failwith "diverge")) in true)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants